#include "modbustcp.h"

ModbusTcp::ModbusTcp(QObject *parent)
    : QObject(parent),
      modbusDevice(nullptr),
      m_coils(RowCount,false),
      m_holdingRegisters(RowCount, 0u)
{
    qDebug() << "ModbusTcp() thread " << this->thread();
}


bool ModbusTcp::connectToHost(QString type, QUrl url){

//    if (modbusDevice != nullptr){
//        modbusDevice->disconnectDevice();
//        delete modbusDevice;
//        modbusDevice = nullptr;
//    }

    if(type == "Serial"){
//        modbusDevice = new QModbusRtuSerialMaster();

//        //Serial function needs to refine
//        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
//                                             url.port());
//        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
//                                             m_settings.parity);
//        modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
//                                             m_settings.baud);
//        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
//                                             m_settings.dataBits);
//        modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
//                                             m_settings.stopBits);
    }
    else if(type == "TCP"){
        //modbusDevice = new QModbusTcpClient();
        modbusDevice.setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
        modbusDevice.setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
    }

    modbusDevice.setTimeout(m_settings.responseTime);
    modbusDevice.setNumberOfRetries(m_settings.numberOfRetries);
    if (!modbusDevice.connectDevice()) {
        qDebug() << "Connect failed: " + modbusDevice.errorString();
    } else {
        qDebug() << "Connect successed";
    }

    return true;
}

bool ModbusTcp::disconnectToHost(){

//    if (modbusDevice) {
//        modbusDevice->disconnectDevice();
//        delete modbusDevice;
//        modbusDevice = nullptr;
//    }

    return true;
}

bool ModbusTcp::readData(int type){

//    if (!modbusDevice){
//        qDebug() << "Please Connect ModbusTcp Master Firstly!";
//        return false;
//    }

    if (auto *reply = modbusDevice.sendReadRequest(readRequest(type),server_address)) {

        if (!reply->isFinished()){

            //readReady(reply);
          connect(reply, &QModbusReply::finished, this, &ModbusTcp::readReady);
        }
        else{
          delete reply; // broadcast replies return immediately
        }
    } else {
        qDebug() << "Connect failed: " + modbusDevice.errorString();
        //statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
    return true;
}

void ModbusTcp::readReady(){
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        for (uint i = 0; i < unit.valueCount(); i++) {
            const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress())
                    .arg(QString::number(unit.value(i),
                                         unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
            qDebug() << "i: " << i
                     << " entry: " << entry;
            //ui->readValue->addItem(entry);
        }
    } else if (reply->error() == QModbusDevice::ProtocolError) {

        qDebug() << tr("Read response error: %1 (Mobus exception: 0x%2)").
                    arg(reply->errorString()).
                    arg(reply->rawResult().exceptionCode(), -1, 16);
        //statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
        //                         arg(reply->errorString()).
        //                        arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    } else {

        qDebug() << tr("Read response error: %1 (code: 0x%2)").
                    arg(reply->errorString()).
                    arg(reply->error(), -1, 16);
        //statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
        //                         arg(reply->errorString()).
        //                         arg(reply->error(), -1, 16), 5000);
    }

    reply->deleteLater();
}


bool ModbusTcp::writeData(int type){

//    if (!modbusDevice)
//        return false;

    qDebug() << "ModbusTcp::writeData thread " << this->thread();

    QModbusDataUnit writeUnit = writeRequest(type);

    QModbusDataUnit::RegisterType table = writeUnit.registerType();

    m_coils[5] = 1;
    for (uint i = 0; i < writeUnit.valueCount(); i++) {
        if (table == QModbusDataUnit::Coils)
            writeUnit.setValue(i, m_coils[i + writeUnit.startAddress()]);
        else
            writeUnit.setValue(i, m_holdingRegisters[i + writeUnit.startAddress()]);
    }

    if (auto *reply = modbusDevice.sendWriteRequest(writeUnit, server_address)) {
        if (!reply->isFinished()) {

            connect(reply, &QModbusReply::finished, this, [this, reply]() {
                if (reply->error() == QModbusDevice::ProtocolError) {
                    qDebug() << tr("Write response error: %1 (Mobus exception: 0x%2)")
                                .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16);
                    //statusBar()->showMessage(tr("Write response error: %1 (Mobus exception: 0x%2)")
                    //                         .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16),
                    //                         5000);
                } else if (reply->error() != QModbusDevice::NoError) {
                    qDebug() << tr("Write response error: %1 (code: 0x%2)").
                                arg(reply->errorString()).arg(reply->error(), -1, 16);
                    //statusBar()->showMessage(tr("Write response error: %1 (code: 0x%2)").
                    //                         arg(reply->errorString()).arg(reply->error(), -1, 16), 5000);
                }
                reply->deleteLater();
            });
        } else {
            // broadcast replies return immediately
            reply->deleteLater();
        }
    } else {

        qDebug() << tr("Write error: ") + modbusDevice.errorString();
        //statusBar()->showMessage(tr("Write error: ") + modbusDevice->errorString(), 5000);
    }
    return true;
}

QModbusDataUnit ModbusTcp::readRequest(int type) const {
    const auto table =
            static_cast<QModbusDataUnit::RegisterType> (type);

    int startAddress = 0;//ui->readAddress->value();
    //Q_ASSERT(startAddress >= 0 && startAddress < 10);

    // do not go beyond 10 entries
    int numberOfEntries = 50;//qMin(ui->readSize->currentText().toInt(), 10 - startAddress);
    return QModbusDataUnit(table, startAddress, numberOfEntries);
}

QModbusDataUnit ModbusTcp::writeRequest(int type) const {
    const auto table =
            static_cast<QModbusDataUnit::RegisterType> (type);

    int startAddress = 0;
    //Q_ASSERT(startAddress >= 0 && startAddress < 10);

    // do not go beyond 10 entries
    int numberOfEntries = 10; //qMin(ui->writeSize->currentText().toInt(), 10 - startAddress);
    return QModbusDataUnit(table, startAddress, numberOfEntries);
}


void ModbusTcp::getCoilsData(int address, quint8 data){

    Q_ASSERT(m_coils.size() > address);
    data = m_coils[address];
}

void ModbusTcp::getHoldingRegistersData(int address, quint16 data){
    Q_ASSERT(m_holdingRegisters.size() > address);
    data = m_holdingRegisters[address];
}
